package com.htfyun.hardware;

import android.hardware.htfyun.IKeyEventListener;
import android.hardware.htfyun.IKeyEventService;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;

import com.htfyun.hardware.reflect.Utils;

//import android.os.ServiceManager;

public class KeyEventController {
    private static final String TAG = "KeyEventManager";


    private static KeyEventController sInstance = null;
    private IKeyEventService mService = null;

    private final ArrayMap<KeyEvent.Callback, Handler> mCallbackMap =
            new ArrayMap<KeyEvent.Callback, Handler>();
    private KeyEventListener mKeyEventListener;

    private final Object mLock = new Object();

    private KeyEvent.DispatcherState mKeyDispatchState = new KeyEvent.DispatcherState();

    /*
     * @hide
     */
    private KeyEventController() {
        //IBinder binder = ServiceManager.getService("keyEventService");
        IBinder binder = Utils.getSystemServiceReflect("keyEventService");
        if (binder == null) {
            Log.e(TAG, "Could not retrieve keyEventService");
            return;
        }
        mService = IKeyEventService.Stub.asInterface(binder);
    }

    public static synchronized KeyEventController getInstance() {
        if (sInstance == null) {
            sInstance = new KeyEventController();
        }
        return sInstance;
    }

    public void registerKeyEventListener(KeyEvent.Callback cb, Handler handler) {
        if (mService == null) {
            return;
        }
        if (handler == null) {
            Looper looper = Looper.getMainLooper();
            if (looper == null) {
                throw new IllegalArgumentException(
                        "No handler given, and current thread has no looper!");
            }
            handler = new Handler(looper);
        }

        synchronized (mLock) {
            Log.i(TAG, "addTouchEventListener");
            mCallbackMap.put(cb, handler);
        }

        if (mKeyEventListener == null) {
            mKeyEventListener = new KeyEventListener();
            try {
                mService.registerKeyEventListener(mKeyEventListener);
            } catch (RemoteException e) {
                Log.e(TAG, "RemoteException in registerKeyEventListener", e);
                mKeyEventListener = null;
            }
        }
    }

    public void unregisterKeyEventListener(KeyEvent.Callback cb) {
        if (mService == null) {
            return;
        }

        synchronized (mLock) {
            Log.i(TAG, "unregisterKeyEventListener");
            mCallbackMap.remove(cb);
        }

        if (mCallbackMap.isEmpty()) {
            if (mKeyEventListener != null) {
                try {
                    mService.unregisterKeyEventListener(mKeyEventListener);
                } catch (RemoteException e) {
                    Log.e(TAG, "RemoteException in unregisterKeyEventListener", e);
                }
                mKeyEventListener = null;
            }
        }
    }

    /*
     *@param keyCode
     *@return
     *{@link Deprecated}, please use {{@link #getKeyState(RK_KEYS)}
     */
    public boolean getKeyState(int keyCode) {
        if (mService == null) {
            return false;
        }
        try {
            return mService.getKeyState(keyCode);
        } catch (RemoteException e) {
            // TODO: handle exception
        }

        return false;
    }

    /**
     * start recovery by broadcast;
     */
    public void startRecovery() {
        if (mService == null) {
            return;
        }
        try {
            mService.startRecovery();
        } catch (RemoteException e) {
            // TODO: handle exception
        }
    }

    /**
     * start recovery with dialog, if to recovery decided by user;
     */
    public void startRecoveryDialog() {
        if (mService == null) {
            return;
        }
        try {
            mService.startRecoveryDialog();
        } catch (RemoteException e) {
            // TODO: handle exception
        }
    }


    public boolean getKeyState(RK_KEY key) {
        return getKeyState(key.getKeyCode());
    }

    public RK_KEY getRKKeyByKeyCode(int keycode) {
        for (RK_KEY keysAttr : RK_KEY.values()) {
            if (keysAttr.getKeyCode() == keycode) {
                return keysAttr;
            }
        }

        return null;
    }

    private class KeyEventListener extends IKeyEventListener.Stub {

        @Override
        public IBinder asBinder() {
            return this;
        }

        @Override
        public void onKeyEvent(final KeyEvent event) {
            int callbackCount = mCallbackMap.size();
            for (int i = 0; i < callbackCount; i++) {
                Handler handler = mCallbackMap.valueAt(i);
                final KeyEvent.Callback cb = mCallbackMap.keyAt(i);
                handler.post(
                        new Runnable() {
                            @Override
                            public void run() {
                                event.dispatch(cb, mKeyDispatchState, cb);
                            }
                        });
            }
        }
    }

    public enum RK_KEY {

        top("top", KeyEvent.KEYCODE_F1),
        left("left", KeyEvent.KEYCODE_F2),
        right("right", KeyEvent.KEYCODE_F3),
        recovery("recovery", KeyEvent.KEYCODE_F6),
        power("power", KeyEvent.KEYCODE_POWER),

        ;
        private final int key_code;
        private final String key_label;
        private RK_KEY(String label, int code) {
            this.key_label = label;
            this.key_code = code;
        }

        public int getKeyCode() {
            return key_code;
        }

        public String getKeyLabel() {
            return key_label;
        }
    }

}
